﻿package sa.huang;

import hpi_iaas.choreography.schemas.choreography.grounding._2006._12.*;


import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;

/**TODO
 * 
 * @author huang
 *
 */
public class GroundingHandler {
	

	@SuppressWarnings("unchecked")
	public void analyzeMessageLinksInGrounding(Data data){
		TMessageLinks tmls = grounding.getMessageLinks();	
		for(TMessageLink tml : tmls.getMessageLink()){
		
			
			ml = tml.getName().getLocalPart();				// name of TMessageLink in grounding.xsd is set to QName, why?
			pt = tml.getPortType();

			data.setPortTypeSet(pt);
			
			
			data.setNsprefix_PTMap(pt);
			
			o = new QName(pt.getNamespaceURI(), tml.getOperation(), pt.getPrefix());
			
			mc1 = data.getConstructs_MLMap().get(ml).get(0);
			mc2 = data.getConstructs_MLMap().get(ml).get(1);
			
			data.setPortType_MCMap(mc1, pt);
			data.setPortType_MCMap(mc2, pt);
			
			data.setOperation_MCMap(mc1, o);
			data.setOperation_MCMap(mc2, o);
			
			/**
			 * (A, b) = parefs_ML(ml);
			 * according Reimann's SA, A is supposed to store sender or senders, b is supposed to store receiver; 
			 * and in Definition 3.27 (the relation Comm ((A, c), (b, d)) ), A is always considered as a set of pas. 
			 * in this implementation, the identifier of "sender" is String, and "senders" is stored as ArrayList<String>
			 * so when i put the (ml, (sender(s), receiver)) pair into parefs_MLMap, 
			 * i put both sender and senders into a ArrayList<String>
			 * 
			 */
			A = (ArrayList<String>) data.getParefs_MLMap().get(ml).get(0);			
			b = (String) data.getParefs_MLMap().get(ml).get(1);
			
			/**
			 * if bindSenderTo_ML(ml) != null then  A = {bindSenderToML(ml)};
			 * if bindSenderTo_ML(ml) != null, i suppose that there must be "senders" in this ml, 	--> this leads to my question 2
			 * the statement above: (A, b) = parefs_ML(ml);
			 * will assign this senders to A
			 * the statement A = {bindSenderToML(ml)} will set the value of A to bindSenderTo_ML(ml)
			 * e.g. 
			 * in ml OrderTransportReplyLink, the value of bindSenderTo is currentDriver
			 * after (A, b) = parefs_ML(ml); A is set to [TaxiTransmitter]
			 * after A = {bindSenderToML(ml)}; A is set to [currentDriver]
			 * 
			 * it means that after statements: if bindSenderTo_ML(ml) != null then  A = {bindSenderToML(ml)}
			 * A will always only contain a single pa.    											--> this leads to my Question 4
			 */
			if(data.getBindSenderTo_MLMap().get(ml) != null){
				//System.out.println(ml + " " + data.getBindSenderTo_MLMap().get(ml));
				A.set(0, data.getBindSenderTo_MLMap().get(ml));
			}
			
			showResult(data);
	
			traverseComm(data);
			
		}
	}
	/**
	 * after the previous statements before traverseComm(data); are executed
	 * we have got sender A; receiver b; portType pt(could only be used by b !) and so on 	--> Question 3
	 * for each ml, these 3 variables (A, b, pt) will be compared to each comm in CommSet
	 * during the while loop of traversing the commSet, only 1 condition could be matched
	 * the while loop will break when the matched condition is found
	 * the switch statement will then execute the corresponding case
	 * @param data
	 */
	private void traverseComm(Data data) {
		/*
		 * during the treaverseComm(), new Comm will be added to commSet, we need to create a new commSet
		 * add all comms of original commSet into this new commSet, traverse this new commSet
		 * the new comm that need to be added, will be added to the original commSet in object data. 
		 */
		HashSet<Comm> currentCommSet = new HashSet<Comm>();	
		currentCommSet.addAll(data.getCommSet());
		
		Iterator<Comm> i = currentCommSet.iterator();
		int condition = 0;
			
		Comm selectedComm = new Comm();		// used to pass the selected comm to switch statement
					int loop=1;
					showCommSet(currentCommSet);
					
		do {
			if(currentCommSet.isEmpty()){
					System.out.println(" CommSet is empty");				
				break;
			} 				
					System.out.println(loop + ". loop start");
							
			Comm currentComm = i.next();
			showComm(currentComm);
							
			if((currentComm.getA().equals(A))																			// currentComm's sender is A
					&& ((data.getPortTypeSet().contains(currentComm.getA_pt())) || (currentComm.getA_pt()==null))		// ((currentComm's sender_pt is c) && ((PT contains c)) || (c == null))
					&& (currentComm.getB().equals(b))																	// currentComm's receiver is b
					&& ((currentComm.getB_pt().equals(pt)))){															// currentComm's receiver_pt is pt
														// (1): there is one comm in Comm: (comm= ((A, c), (b, pt)) && ((PT contains c)) || (c == null))
						condition = 1; 
						selectedComm = currentComm;
							loop++;
							System.out.print("	 "); showComm(currentComm); System.out.println("	  matches condition " + condition);
						break;
			
			} else if((A.size() == 1)&&(data.getParticipantSet().contains(A.get(0)))									// A={a}∧a∈Pa : A only contains 1 element a and a is in participantSet 
					&&((currentComm.getA().size() == 1)&&(currentComm.getA().get(0).equals(b)))							// currentComm's sender only contains 1 element b
					&&((currentComm.getA_pt() != null)&&(currentComm.getA_pt().equals(pt))||((currentComm.getA_pt() == null)&&(pt == null)))	// currentComm's sender_pt is pt
					&&(currentComm.getB().equals(A.get(0)))																// currentComm's receiver is a
					&&(data.getPortTypeSet().contains(currentComm.getB_pt()))){											// currenComm's receiver_pt is in portTypeSet
														// (2): ¬(1) ∧ A={a} ∧ a∈Pa ∧ ∃comm∈Comm:(comm= (({b}, pt), (a,d)) ∧ d∈PT)
						condition = 2;
						selectedComm = currentComm;
							System.out.print("	 "); showComm(currentComm); System.out.println("	  matches condition " + condition);
							loop++;
						break;
				} else if((A.size() == 1)&&(data.getParticipantSet().contains(A.get(0)))								// A={a}∧a∈Pa : A only contains 1 element a and a is in participantSet
						&&((currentComm.getA().size() == 1)&&(currentComm.getA().get(0).equals(b))						// currentComm's sender only contains 1 element b
						&&(currentComm.getA_pt() == null)																// currentComm's sender_pt is ⊥
						&&(currentComm.getB().equals(A.get(0)))															// currentComm's receiver is a
						&&(data.getPortTypeSet().contains(currentComm.getB_pt())))){									// (currentComm's receiver_pt is d)  ∧ (d ∈ PT)
														// (3): ¬((1)∨(2)) ∧ A={a} ∧ a∈Pa ∧ ∃comm∈Comm:(comm = (({b}, ⊥), (a,d)) ∧ d∈PT)
						condition = 3;
						selectedComm = currentComm;
							System.out.print("	 "); showComm(currentComm); System.out.println("	  matches condition " + condition);
							loop++;
						break;
				} else {
							System.out.println("	currentComm doesn't matches condition 1,2,3");
				}
			
				System.out.println(loop++ + ". loop end");

		}while(i.hasNext());
				System.out.println("entire loop end");
				
		switch(condition){
		case 1:
				System.out.println("case 1 is executed");
																										//(pl1, pl2) ← partnerLinks_Comm(comm);
			data.setPartnerLink_MCMap(mc1, data.getPartnerLinks_CommMap().get(selectedComm).get(0));	//partnerLink_MC(mc1) ← pl1;
			data.setPartnerLink_MCMap(mc2, data.getPartnerLinks_CommMap().get(selectedComm).get(1));	//partnerLink_MC(mc2) ← pl2;
			break;
		case 2:
				System.out.println("case 2 is executed");
																										//(pl1, pl2) ← partnerLinks Comm (comm);
			data.setPartnerLink_MCMap(mc1, data.getPartnerLinks_CommMap().get(selectedComm).get(1));	//partnerLink_MC(mc1) ← pl2;
			data.setPartnerLink_MCMap(mc2, data.getPartnerLinks_CommMap().get(selectedComm).get(0));	//partnerLink_MC(mc2) ← pl1;
			break;
		case 3:
				System.out.println("case 3 is executed");
																										//(pl1, pl2) ← partnerLinks Comm (comm);
			data.setPartnerLink_MCMap(mc1, data.getPartnerLinks_CommMap().get(selectedComm).get(1));	//partnerLink_MC(mc1) ← pl2;
			data.setPartnerLink_MCMap(mc2, data.getPartnerLinks_CommMap().get(selectedComm).get(0));	//partnerLink_MC(mc2) ← pl1;
			
			selectedComm.setA_pt(pt);																//comm.changeFirstPortType(pt);
			data.setMyRole_PLMap(data.getPartnerLinks_CommMap().get(selectedComm).get(0), b);		//myRole_PL(pl1) ← b;
			data.setPartnerRole_PLMap(data.getPartnerLinks_CommMap().get(selectedComm).get(1), b);		//parnterRole_PL(pl2) ← b;
			break;
		case 4:
				System.out.println("case 4 is executed");
			Comm comm = new Comm(A,null,b,pt);
			data.setCommSet(comm);
			createPartnerLinkDeclarations(comm, data);
			break;
		case 5:
				System.out.println("case 5 is executed");
			Comm comm1 = new Comm(A,null,b,pt);
			data.setCommSet(comm1);
			createPartnerLinkDeclarations(comm1, data);
			break;
			
			default :						// (6): ¬((1)∨(2)∨(3)∨(4)∨(5))	* also includes the condition that there is currently no comm in commSet*
					System.out.println("default is executed");
				Comm comm11 = new Comm(A,null,b,pt);
				data.setCommSet(comm11);
				createPartnerLinkDeclarations(comm11, data);
				break;				
		}		
		
		showPartnerLinks_Comm(data);
		showPartnerLinks_Scope(data);
		showPartnerRole_PL(data);
		showPLT_COMM(data);
							
	}



	private void createPartnerLinkDeclarations(Comm comm, Data data) {
		String pl1, pl2;
		String plt;
		String senders_ids = A.get(0);									// senders_ids ← A[1];
		QName sc;
		/**
		 * Question 4: will these statements ever be executed?
		 */
		for(String a : A.subList(1, A.size())){							// for all a∈A\A[1] do			
			senders_ids.concat("_" + a);								// senders_ids ← senders_ids + "_" + a;
		}
		
		
		pl1 = senders_ids.concat("-" + b + "_isRealizedBy_" + replaceColons(pt));				// pl1 ← senders_ids + "-" + b + "_isRealizedBy_" + replaceColons(pt);
		pl2 = b.concat("_isRealizedBy_"  + replaceColons(pt) + "-" + senders_ids);				// pl2 ← b + "_isRealizedBy_" + replaceColons(pt) + "-" + senders_ids;
		data.setPartnerLinkSet(pl1);															// PL ← PL ∪ pl1;
		data.setPartnerLinkSet(pl2);															// PL ← PL ∪ pl2;
		
		plt = senders_ids.concat("-" + b + "_isRealizedBy_" + replaceColons(pt) + "-plt");		// plt ← senders_ids + "-" + b + "_isRealizedBy_" + replaceColons(pt) + "-plt";
		data.setPartnerLinkTypeSet(plt);														// PLType ← PLType ∪ plt;
		
		data.setPartnerLink_MCMap(mc1,pl1);														// partnerLink_MC(mc1) ← pl1;
		data.setPartnerLink_MCMap(mc2,pl2);														// partnerLink_MC(mc2) ← pl2;
		
		if(data.getScope_PaMap().get(b) == null){												// if scope_Pa(b) = ⊥ then
			sc = data.getProcess_PaTypeMap().get(data.getPaType_PaMap().get(A.get(0)));			// sc ← process_PaType(type_Pa(A[1]));
		} else {
			sc = data.getScope_PaMap().get(b);													// sc ← scope_Pa_(b);
		}		
		data.setPartnerLinks_scopeMap(sc, pl1);													// partnerLinks_scope(sc) ← partnerLinks_scope(sc) ∪ pl1;
		
		if((data.getBindSenderTo_MLMap().get(ml) == null) || (data.getScope_PaMap().get(data.getBindSenderTo_MLMap().get(ml)) == null)){ 
																								// if bindSenderTo_ML(ml) = ⊥ ∨ scope_Pa(bindSenderTo_ML(ml)) = ⊥ then
			sc = data.getProcess_PaTypeMap().get(data.getPaType_PaMap().get(b));				// sc ← process_PaType(patype_Pa(b));			
		} else {
			sc = data.getScope_PaMap().get(data.getBindSenderTo_MLMap().get(ml));				// sc ← scope_Pa(bindSenderTo_ML(ml));
		}
		data.setPartnerLinks_scopeMap(sc, pl2);													// partnerLinks_scope(sc) ← partnerLinks_scope(sc) ∪ pl2;
		
		data.setPLType_PLMap(pl1, plt);															// type_PL(pl1) ← plt;
		data.setPLType_PLMap(pl2, plt);															// type_PL(pl2) ← plt;
		data.setPartnerLinks_CommMap(comm, pl1, pl2);											// partnerLinks_Comm(comm) ← (pl1, pl2);
		data.setPlt_CommMap(comm, plt);															// plt_Comm(comm) ← plt;
		data.setMyRole_PLMap(pl1, null);														// myRole_PL(pl1) ← ⊥;
		data.setMyRole_PLMap(pl2, b);															// myRole_PL(pl2) ← b;
		data.setPartnerRole_PLMap(pl1, b);														// partnerRole_PL(pl1) ← b;
		data.setPartnerRole_PLMap(pl2, null);													// partnerRole_PL(pl2) ← ⊥;


	}
	
	public void analyzePropertiesInGrounding(Data data){
		TProperties tprops = grounding.getProperties();
		for(TProperty tprop: tprops.getProperty()){
			String propName = tprop.getName();
			QName property = tprop.getWSDLproperty();
			data.setCorrPropNameSet(propName);
			data.setPropertySet(property);
			data.setProperty_CorrPropNameMap(propName, property);
			data.setNsprefix_PropertyMap(property, property.getPrefix());
		}
	}
	
	
	
	private String replaceColons(QName pt){
		return pt.getPrefix().concat("_"+pt.getLocalPart());
	}
	
	 //The following methods show*** are used to show the data stored in the object data 
	
	private void showPLT_COMM(Data data){
		for(String plt: data.getPartnerLinkTypeSet()){
			Comm comm = data.comm_plt(plt);
			showComm(comm);
		}
	}
	
	private void showComm(Comm currentComm) {
		System.out.println( " currentComm: " + currentComm.getA().toString() + ", " + currentComm.getA_pt() + ", " + currentComm.getB() + ", " + currentComm.getB_pt());
	}
	private void showCommSet(HashSet<Comm> currentCommSet) {
		System.out.println(" CommSet now contains " + currentCommSet.size() + " comms:");
		Iterator<Comm> i = currentCommSet.iterator();
		while(i.hasNext()){
			Comm currentComm = (Comm)i.next();
				
			System.out.println("   " + currentComm.getA().toString() + ", " + currentComm.getA_pt() + ", " + currentComm.getB() + ", " + currentComm.getB_pt());
		}
	}
	private void showPartnerLinks_Comm(Data data){
		Iterator<Comm> iter = data.getPartnerLinks_CommMap().keySet().iterator();
		while(iter.hasNext()){
			Comm key = iter.next();
			System.out.println(" pls of comm:");
			System.out.println(data.getPartnerLinks_CommMap().get(key).get(0));
			System.out.println(data.getPartnerLinks_CommMap().get(key).get(1));
		}	
	}
	private void showPartnerLinks_Scope(Data data){
		Iterator<QName> iter = data.getPartnerLinks_scopeMap().keySet().iterator();
		while(iter.hasNext()){
			QName key = iter.next();
			System.out.println(" there are scopes/processes: "+key);
			HashSet<String> sc = data.getPartnerLinks_scopeMap().get(key);
			for(Iterator<String> iter_1 = sc.iterator(); iter_1.hasNext();){
				System.out.println( " pls should be declared in these scopes/processes: "+iter_1.next());
			}
		}
	}
	private void showPartnerRole_PL(Data data){
		Iterator<String> iter = data.getPartnerRole_PLMap().keySet().iterator();
		while(iter.hasNext()){
			String key = iter.next();
			System.out.println("  pl: "+key);
			String value = data.getPartnerRole_PLMap().get(key);
			System.out.println("      has partnerRole: "+value);
		}
	}
	public void showProperty(Data data){
		System.out.println("  property_CorrPropName: ");
		Iterator<String> iter = data.getProperty_CorrPropNameMap().keySet().iterator();
		while(iter.hasNext()){
			String key = iter.next();
			System.out.println("  corrPropName: "+key);
			QName value = data.getProperty_CorrPropNameMap().get(key);
			System.out.println("      has property: "+value);
		}
	}
	
	private void showResult(Data data){
		System.out.println();
		System.out.println("-->data from currentml " + ml + ":    A: " + A + " b: " + b + " pt:" + pt);
	}


	

	
	public GroundingHandler() {
		try {
			jaxbContext = JAXBContext.newInstance("hpi_iaas.choreography.schemas.choreography.grounding._2006._12");
		} catch (JAXBException e) {
			System.out.println(e);
		}
		//grounding = loadGrounding("src/main/resources/BPEL4Chor/taxiApp/grounding.xml");
		grounding = loadGrounding("src/main/resources/BPEL4Chor/travelExample/TravelExamplegrounding.xml");

	}
	public TGrounding loadGrounding(String filePath) {		
		try {
			Unmarshaller u = jaxbContext.createUnmarshaller();
			grounding = (TGrounding)((JAXBElement<?>) u.unmarshal( new FileInputStream(filePath))).getValue();			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JAXBException e) {
			e.printStackTrace();
		}
		//analyzePaAndPaTypeInTopology();
		return grounding;
	}
	private JAXBContext jaxbContext;
	private TGrounding grounding = null;
	
	private String ml = null;
	private QName pt = null;
	private QName o = null;
	private QName mc1, mc2;
	private ArrayList<String> A;
	private String b;
	

}
